/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#include "gimimessagefilter.h"

namespace gimi {

WantedService::WantedService()
  : description (),
    location ()
{

}

WantedService::WantedService(const std::string descr,
                             const std::string loc)
  : description (descr),
    location (loc)
{

}

WantedService::~WantedService()
{
  
}
  
GIMIMessageFilter::GIMIMessageFilter()
  : filterMutex (ownMutex_Init()),
    wantedSenders (),
    wantedTypes ()
{
  assert(filterMutex != NULL);
}

GIMIMessageFilter::~GIMIMessageFilter()
{
  ownMutex_Destroy(filterMutex);
}

bool GIMIMessageFilter::addWantedSender(const int clientId)
{
  ownMutex_Lock(filterMutex);
  bool result = (wantedSenders.insert(clientId)).second;
  if(result) {
    dPrint(14, "Added sender %d to wanted senders.", clientId);
  } else {
    dPrint(4, "ClientId %d already added.", clientId);
  }
  ownMutex_Unlock(filterMutex);

  return result;
}
    
bool GIMIMessageFilter::removeWantedSender(const int clientId)
{
  ownMutex_Lock(filterMutex);
  bool result = wantedSenders.erase(clientId);
  if(result) {
    dPrint(14, "Removed sender %d from wanted senders.", clientId);  
  } else {
    dPrint(7, "ClientId %d could not be removed. Not in list.", clientId);
  }
  ownMutex_Unlock(filterMutex);

  return result;
}

bool GIMIMessageFilter::clearWantedSenders()
{
  ownMutex_Lock(filterMutex);
  wantedSenders.clear();
  ownMutex_Unlock(filterMutex);
  return true;
}

bool GIMIMessageFilter::isWantedSender(const int clientId)
{
  ownMutex_Lock(filterMutex);
  bool accept = false;
  if (wantedSenders.empty()) { // Wanted senders empty, accept all.
    dPrint(18, "No senders defined, accept all.");
    accept = true;
  } else {
    if (wantedSenders.end() == wantedSenders.find(clientId)) { // Not found
      dPrint(18, "Sender %d was not found in list.", clientId);
      accept = false;
    } else {
      dPrint(18, "Sender %d is wanted sender.", clientId);
      accept = true;
    }
  }
  ownMutex_Unlock(filterMutex);
  
  return accept;
}

bool GIMIMessageFilter::isRegisteredType(const int majorTypeId,
                                         const int minorTypeId)
{
  bool result = false;

  ownMutex_Lock(filterMutex);

  // Search for Major number
  t_wantedTypesIterator majoriter = wantedTypes.find(majorTypeId);

  // Got Majornumber?
  if (majoriter != wantedTypes.end()) { // Major-type nfound.
    // Yes. Search for minor under the found Major
    t_wantedMinorTypesIterator minoriter = 
      (majoriter->second).find(minorTypeId);
    
    // If Entry found, mark result true
    // (No direct store, optimized to prefer read instruction -> Use 'if')
    if (minoriter != (majoriter->second).end()) 
      result = true;
  }

  ownMutex_Unlock(filterMutex);
  
  return result;
}

bool GIMIMessageFilter::addWantedType(const int majorTypeId,
                                      const int minorTypeId /* = GIMI_IGNOREMINORTYPE */,
                                      const std::string serviceDescription /* = "" */,
                                      const std::string serviceLocation /* = "" */)
{
  ownMutex_Lock(filterMutex);
  t_wantedTypesIterator majoriter = wantedTypes.find(majorTypeId);
  t_wantedMinorTypesIterator minoriter;

  if (majoriter == wantedTypes.end()) { // Major-type not found.
    // Add major-type and get the iterator.
    majoriter = (wantedTypes.insert(t_wantedTypesPair(majorTypeId,
                 t_wantedMinorTypes()))).first;
  }

  // Add minor-types if we dont't want to ignore them.
  // Empty minor-type-list means that we accept all minor-types.
  if (minorTypeId != GIMI_IGNOREMINORTYPE) {
    // Try inserting minorTypeId, if fails, then it already exists.
    if (!((majoriter->second).insert(t_wantedMinorTypesPair(minorTypeId,
           WantedService(serviceDescription, serviceLocation))).second))
    { // Insert failed
      ownMutex_Unlock(filterMutex);
      dPrint(4, "Could not add type %d:%d, it already exists.", majorTypeId,
             minorTypeId);
      return false;
    }
  } else { // Ignore minor-types, major-type should then be empty.
    if(!(majoriter->second).empty())
      (majoriter->second).clear();
  }
  ownMutex_Unlock(filterMutex);
  
  dPrint(14, "Added type %d:%d to wanted types.", majorTypeId, minorTypeId);
  return true;
}

bool GIMIMessageFilter::removeWantedType(const int majorTypeId,
                                         const int minorTypeId /* = GIMI_IGNOREMINORTYPE */)
{
  ownMutex_Lock(filterMutex);
  bool result = false;
  t_wantedTypesIterator majoriter = wantedTypes.find(majorTypeId);
  t_wantedMinorTypesIterator minoriter;
  if (majoriter != wantedTypes.end()) { // Major-type found.
    if (minorTypeId != GIMI_IGNOREMINORTYPE) { // Minor-type set.
      minoriter = (majoriter->second).find(minorTypeId);
      if (minoriter == (majoriter->second).end()) { // Minor-type not found.
        dPrint(4, "Did not find wanted-type %d:%d", majorTypeId, minorTypeId);
        result = false;
      } else { // Minor-type found, remove it.
        (majoriter->second).erase(minoriter);
        dPrint(14, "Removed type %d:%d from wanted types.", majorTypeId, minorTypeId);
        result = true;
      }
    } else { // Remove major-type
      wantedTypes.erase(majoriter);
      dPrint(14, "Removed major-type %d from wanted types.", majorTypeId);
      result = true;
    }
  } else { // Major-type not found.
    dPrint(4, "Did not find major-type %d", majorTypeId);
    result = false;
  }
  ownMutex_Unlock(filterMutex);
  
  return result;
}

bool GIMIMessageFilter::clearWantedTypes()
{
  ownMutex_Lock(filterMutex);
  wantedTypes.clear();
  ownMutex_Unlock(filterMutex);
  return true;
}

int GIMIMessageFilter::listWantedTypes(t_serviceList &serviceList)
{
  dPrint(20, "Called");
  t_wantedTypesIterator majorIter;
  t_wantedMinorTypesIterator minorIter;
  int n = 0;

  ownMutex_Lock(filterMutex);
  
  // Go through major-types.
  for (majorIter = wantedTypes.begin();
       majorIter != wantedTypes.end();
       ++majorIter)
  {
    if ((majorIter->second).empty()) { // Ignore minor-types.
      serviceList.push_back(GIMIService(0, "", GIMI_SERVICETYPE_ACCEPTED,
                            majorIter->first, GIMI_IGNOREMINORTYPE));
      ++n;
    } else { // Go through minor-types.
      for (minorIter = (majorIter->second).begin();
           minorIter != (majorIter->second).end();
           ++minorIter)
      {
        serviceList.push_back(GIMIService(0, "", GIMI_SERVICETYPE_ACCEPTED,
                              majorIter->first, minorIter->first,
                              (minorIter->second).getDescription(),
                              (minorIter->second).getLocation()));
        ++n;
      }
    }
  }

  ownMutex_Unlock(filterMutex);
  
  return n;
}

bool GIMIMessageFilter::isWantedType(const int majorTypeId,
                                     const int minorTypeId)
{
  bool result = false;
  
  ownMutex_Lock(filterMutex);
  if (wantedTypes.empty()) { // No types specified, accept all.
    dPrint(18, "No wanted types defined, accept all.");
    //    ownMutex_Unlock(filterMutex);
    result = true;
  } else {
    t_wantedTypesIterator majoriter = wantedTypes.find(majorTypeId);
    if (majoriter == wantedTypes.end()) { // Major-type not found.
      dPrint(18, "Was not wanted type. Major-type %d not found.", majorTypeId);
      //  ownMutex_Unlock(filterMutex);
      result = false;
    } else {
      if ((majoriter->second).empty()) { // Major-type has no minor-types, accept all.
        dPrint(18, "Major-type %d was empty, accept all minor-types.", majorTypeId);
        // ownMutex_Unlock(filterMutex);
        result = true;
      } else {
        t_wantedMinorTypesIterator minoriter = (majoriter->second).find(minorTypeId);
        if (minoriter == (majoriter->second).end()) { // Minor-type not found.
          dPrint(18, "Was not wanted type. Minor-type %d not found in major-type %d.",
                minorTypeId, majorTypeId);
          // ownMutex_Unlock(filterMutex);
          result = false;
        } else {
          dPrint(18, "Type %d:%d found in wanted list.", majorTypeId, minorTypeId);
          result = true;
        }
      }
    }
  }
  ownMutex_Unlock(filterMutex);

  return result;
}

}
